package com.wargon.securitycommon.util;

import com.wargon.securitycommon.bo.RSAKeyBo;

import javax.crypto.Cipher;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;


public class RSAUtil {

    private static final String ALGORITHM = "RSA";
    private static final String TRANSFORMATION = "RSA/ECB/PKCS1Padding";
    private static final int KEY_SIZE = 2048;
    private static final int MAX_ENCRYPT_BLOCK = KEY_SIZE / 8 - 11; // RSA最大加密明文大小
    private static final int MAX_DECRYPT_BLOCK = KEY_SIZE / 8; // RSA最大解密密文大小
    private static final String CHARSET_NAME = "UTF-8";
    private static final String BLOCK_DELIMITER = ";";


    public static RSAKeyBo generateRSAKeyDto() {
        RSAKeyBo rsaKeyBo = new RSAKeyBo();
        KeyPair keyPair = generateKeyPair();
        rsaKeyBo.setPrivateKey(keyPair.getPrivate());
        rsaKeyBo.setPubicKey(keyPair.getPublic());
        return rsaKeyBo;
    }

    /**
     * 生成RSA密钥对
     */
    public static KeyPair generateKeyPair() {
        KeyPairGenerator keyGen = null;
        try {
            keyGen = KeyPairGenerator.getInstance(ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        keyGen.initialize(KEY_SIZE);
        return keyGen.generateKeyPair();
    }

    /**
     * 公钥加密
     *
     * @param data      待加密的数据
     * @param publicKey 公钥
     * @return 加密后的字符串
     * @throws Exception 异常
     */
    public static String encrypt(String data, PublicKey publicKey){
        try{
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] dataBytes = data.getBytes(CHARSET_NAME);
            int inputLen = dataBytes.length;
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < inputLen; i += MAX_ENCRYPT_BLOCK) {
                int length = Math.min(inputLen - i, MAX_ENCRYPT_BLOCK);
                byte[] encryptedBlock = cipher.doFinal(dataBytes, i, length);
                sb.append(Base64.getEncoder().encodeToString(encryptedBlock));
                if (i + MAX_ENCRYPT_BLOCK < inputLen) {
                    sb.append(BLOCK_DELIMITER); // 使用换行符分隔不同块
                }
            }

            return sb.toString();
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 私钥解密
     *
     * @param encryptedData 已加密的数据
     * @param privateKey    私钥
     * @return 解密后的字符串
     * @throws Exception 异常
     */
    public static String decrypt(String encryptedData, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            String[] blocks = encryptedData.split(BLOCK_DELIMITER); // 根据换行符分割不同的块
            StringBuilder sb = new StringBuilder();


            for (String block : blocks) {
                byte[] encryptedBlock = Base64.getDecoder().decode(block);
                byte[] decryptedBlock = cipher.doFinal(encryptedBlock);
                try {
                    sb.append(new String(decryptedBlock, CHARSET_NAME));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
            }

            return sb.toString();
        }catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 将公钥转换为字符串
     */
    public static String getPublicKeyString(PublicKey publicKey) {
        return Base64.getEncoder().encodeToString(publicKey.getEncoded());
    }

    /**
     * 将私钥转换为字符串
     */
    public static String getPrivateKeyString(PrivateKey privateKey) {
        return Base64.getEncoder().encodeToString(privateKey.getEncoded());
    }

    /**
     * 将字符串转换为公钥
     */
    public static PublicKey stringToPublicKey(String publicKeyStr) {
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = null;
        try {
            keyFactory = KeyFactory.getInstance(ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        try {
            return keyFactory.generatePublic(spec);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将字符串转换为私钥
     */
    public static PrivateKey stringToPrivateKey(String privateKeyStr) {
        byte[] keyBytes = Base64.getDecoder().decode(privateKeyStr);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = null;
        try {
            keyFactory = KeyFactory.getInstance(ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        try {
            return keyFactory.generatePrivate(spec);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }
}

